﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class AreaCreator
{
	public delegate void Callback();
	public delegate void CallbackInteractives(int _interactivesFound);
	private static CallbackInteractives finishedScan;

	private static int currentActivation = 0;
	private static List<Tile> area;
	private static List<Tile> path = new List<Tile>();
	private static List<List<Tile>> tileLayers = new List<List<Tile>>();
	private static Dictionary<Interactive, List<Tile>> interactivesPaths = new Dictionary<Interactive, List<Tile>>();
	
	public static void ActivatePath(List<Tile> _path, Tile.State _state, Callback _finishedCallback)
	{
		path = _path;
		GameManager.Instance.StartCoroutine(StartPath(_path, _state, _finishedCallback));
	}

	public static void RemovePath()
	{
		foreach (Tile tile in path)
		{
			tile.SetState(Tile.State.Regular);
		}
	}

	public static void ActivateArea(Tile _center, Character _character, Tile.State _state, CallbackInteractives _callback)
	{
		finishedScan = _callback;

		List<Tile> newArea = Area.WalkArea(_center, (int)_character.GetInfo<CharacterInfo>().speed.minValue);
		RemoveTilesImmediate(ListDifference(area, newArea));
		area = new List<Tile>(newArea);

		CreateTileList(_center, _character);
		
		interactivesPaths.Clear();
		switch (_state)
		{
		case Tile.State.PickUp:	FindAllPaths(_center, Tile.ActionType.PickUp); break;
		case Tile.State.Action:	FindAllPaths(_center, Tile.ActionType.Action); break;
		case Tile.State.Attack:	FindAllPaths(_center, Tile.ActionType.Attack_Zombie); break;
		}

		GameManager.Instance.StopAllCoroutines();
		GameManager.Instance.StartCoroutine(StartTileScan(_state));
		if (_state != Tile.State.Walk)
		{
			GameManager.Instance.StartCoroutine(RemoveTileScan());
		}
	}

	public static void RemoveArea()
	{
		GameManager.Instance.StopAllCoroutines();
		GameManager.Instance.StartCoroutine(RemoveTilesReversed(currentActivation));
	}

	public static List<Tile> GetPath(Interactive _inter)
	{
		if (interactivesPaths.ContainsKey(_inter))
		{
			return interactivesPaths[_inter];
		}

		return new List<Tile>();
	}
	
	private static IEnumerator StartPath(List<Tile> _path, Tile.State _state, Callback _finishedCallback)
	{
		foreach (Tile tile in _path)
		{
			tile.SetState(_state);
			
			yield return new WaitForSeconds(0.05f);
		}
		
		if (_finishedCallback != null)
		{
			_finishedCallback();
		}
		
		//yield return new WaitForSeconds(1f);
	}
	
	private static IEnumerator StartTileScan(Tile.State _state)
	{
		for (int i = 0; i < tileLayers.Count; i++)
		{
			foreach (Tile tile in tileLayers[i])
			{
				tile.SetState(_state);
			}
			
			yield return new WaitForSeconds(0.1f);
		}
		
		if (finishedScan != null)
		{
			finishedScan(interactivesPaths.Count);
		}
	}
	
	private static IEnumerator RemoveTileScan()
	{
		yield return new WaitForSeconds(0.5f);
		for (int i = 0; i < tileLayers.Count; i++)
		{
			foreach (Tile tile in tileLayers[i])
			{
				bool fromPath = false;
				foreach (KeyValuePair<Interactive, List<Tile>> kvp in interactivesPaths)
				{
					if (kvp.Value != null)
					{
						fromPath |= kvp.Value.Contains(tile);
						if (kvp.Value.IndexOf(tile) == kvp.Value.Count - 1)
						{
							kvp.Key.SetAsSelectable(kvp.Value);
						}
					}
				}

				if (!fromPath)
				{
					tile.SetState(Tile.State.Regular);
				}
			}
			
			yield return new WaitForSeconds(0.1f);
		}
	}
	
	private static IEnumerator RemoveTilesReversed(int _currentActivation)
	{
		for (int i = tileLayers.Count - 1; i >= 0; i--)
		{
			foreach (Tile tile in tileLayers[i])
			{
				tile.SetState(Tile.State.Regular);
			}
			
			yield return new WaitForSeconds(0.05f);
		}

		path.Reverse();
		for (int i = path.Count - 1; i >= 0; i--)
		{
			path[i].SetState(Tile.State.Regular);
			
			yield return new WaitForSeconds(0.2f);
		}
	}

	private static void RemoveTilesImmediate(List<Tile> _area)
	{
		foreach (Tile tile in _area)
		{
			tile.SetState(Tile.State.Regular);
		}
	}
	
	private static List<Tile> ListDifference(List<Tile> _listA, List<Tile> _listB)
	{
		if (_listA == null || _listB == null)
		{
			return new List<Tile>();
		}

		List<Tile> tiles = new List<Tile>();
		foreach (Tile tile in _listA)
		{
			if (!_listB.Contains(tile))
			{
				tiles.Add(tile);
			}
		}
		
		return tiles;
	}

	private static void FindAllPaths(Tile _center, Tile.ActionType _type)
	{
		foreach (KeyValuePair<Interactive, List<Tile>> kvp in FloorManager.Instance.InteractivesInArea(area, _type))
		{
			Tile closest = Tile.ClosestTile(_center, kvp.Value);
			interactivesPaths.Add(kvp.Key, Search.GetPath(_center, closest, area));
		}
	}

	private static void CreateTileList(Tile _center, Character _character)
	{
		List<Tile> areaCopy = new List<Tile>(area);
		tileLayers.Clear();
		while (areaCopy.Count > 0)
		{
			float smallestDistance = float.PositiveInfinity;
			foreach (Tile tile in areaCopy)
			{
				float distance = Tile.Distance(_center, tile);
				if (distance < smallestDistance)
				{
					smallestDistance = distance;
				}
			}
			
			List<Tile> tiles = new List<Tile>();
			for (int i = areaCopy.Count - 1; i >= 0; i--)
			{
				float distance = Tile.Distance(_center, areaCopy[i]);
				if (distance <= smallestDistance)
				{
					tiles.Add(areaCopy[i]);
					areaCopy.RemoveAt(i);
				}
			}
			
			tileLayers.Add(tiles);
		}
	}
}
